Question 1: Cumulative Distribution Function (CDF)
Estimation
The following failure times (in hours) were observed for 8 electronic
components:
23, 45, 67, 89, 112, 156, 189, 245
- Write an R function implementing the ECDF \(\hat{F}_n(t)\) according to its
mathematical definition. Validate your implementation using R’s ecdf()
function on the given data, with comparison based on their step
functions.
We are given the following definition for \(\hat{F}_n(t)\)
\[ \hat{F}_n(t) = \frac{1}{n}
\sum_{i=1}^{n} \mathbb{I} \left(t_n < t \right)\] where,
\[ \mathbb{I}(t) =
\begin{cases}
0 & t_n > t\\
1 & t_n \leq t
\end{cases}
\]
random_sample <- c(23, 45, 67, 89, 112, 156, 189, 245)
sams_ecdf <- function(x) {
n <- length(x)
function (t) {
mat <- outer(x, t, FUN="<=")
return (colSums(mat) / n)
}
}
#some tests to check my function behaves correctly
r_lang_ecdf <- stats::ecdf(random_sample)
sam_ecdf <- sams_ecdf(random_sample)
test_points <- seq(20, 250, 1)
results <- r_lang_ecdf(test_points) == sam_ecdf(test_points)
print(paste("fails: ", length(results[!results])))
[1] "fails: 0"
print(paste("passes: ", length(results[results])))
[1] "passes: 231"
x_points <- seq(20, 250, 0.5)
plot <- ggplot(data=data.frame(x=x_points, y_1=sam_ecdf(x_points), y_2=r_lang_ecdf(x_points))) +
#geom_point(aes(x=x, y=y_1, color="Sam's ECDF")) +
#geom_point(aes(x=x, y=y_2, color="R's ECDF"))
geom_line(aes(x=x, y=y_1, color="Sam's ECDF")) +
geom_line(aes(x=x, y=y_2, color="R's ECDF"), linetype="dashed") +
labs(x="failure times", y="cumlative density") +
labs(title="Comparison of ECDF Implimentations")
ggplotly(plot)
- A colleague claims that the probability of failure before 100 hours
is 0.5 based on these data. Do you agree? Explain your reasoning using
the empirical cumulative distribution function (ECDF).
random_sample <- c(23, 45, 67, 89, 112, 156, 189, 245)
r_ecdf <- stats::ecdf(random_sample)
print(r_ecdf(100))
[1] 0.5
Here we compute ECDF(100 hours). This is the porportion of data
values that are less than or equal to 100. This value approximates the
true CDF, the probability that a failure occurs at or before 100 hours.
From the given data we get that ECDF(100) = 0.5, which is what the a
colleague claims is the probability of failure. I agree that from these
data, 0.5 is a reasonable estimation for the probability of failure
before 100 hours.
Question 2: Density Function Estimation
Consider the following failure times from a mechanical system:
12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4
- Create a histogram of the data using 3 equally spaced bins. What is
the estimated density in each bin? Describe the shape of the histogram’s
distribution.
random_sample <- c(12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4)
d_frame <- data.frame(x=random_sample)
plot <- ggplot(data=d_frame, aes(x = x)) +
geom_histogram(aes(y = after_stat(density)),bins=3)
ggplotly(plot)
Here, the histogram has a rough mound shape centered at 18.85.
The density of each bin: bin 1. 0.0458 bin 1. 0.061 bin 3. 0.0458
- Write an R function that computes kernel density estimates using a
Gaussian kernel with \(h=2\). Validate
your implementation against R’s built-in density() function.
\[
\hat{f}_h(t) = \frac{1}{nh}\sum_{i=1}^n K\left( \frac{t-t_i}{h}\right),
\ \ \text{ where } \ \ K(u) = \frac{1}{\sqrt{2\pi}} e^{-u^2/2}.
\]
kernel_density <- function(sample, K=dnorm, h=2) {
n <- length(sample)
function(t, h=2) {
k <- K( outer(t, sample, FUN="-") / h)
s <- rowSums(k)
return (s / n / h)
}
}
x <- c(12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4)
my_gaussian_kernel_density <- kernel_density(x)
r_density <- density(x, bw=2, kernel="gaussian")
plot <- ggplot(data=data.frame(x=r_density$x, y=r_density$y, y_2=my_gaussian_kernel_density(r_density$x))) +
geom_line(aes(x=x, y=y_2, color="sam")) +
geom_line(aes(x=x, y=y, color="r"), linetype="dashed")
ggplotly(plot)
- Write a custom R function that computes kernel density estimates
using the Epanechnikov kernel with \(h=2\). Validate your implementation by
comparing results with R’s built-in density() function for Gaussian
kernel estimation.
\[
\hat{f}_h(t) = \frac{1}{nh}\sum_{i=1}^n K\left( \frac{t-t_i}{h}\right),
\ \ \text{ where } \ \ K(u) = \frac{3}{4}(1 - u^2) \ \ \text{ for } \ \
|u| \le 1.
\]
x <- c(12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4)
K <- function(u) {
mask <- abs(u) <= 1
return (((3/4) * (1 - u^2)) * mask)
}
kernel_density <- function(sample, K=dnorm) {
function(t, h=2) {
n <- length(sample)
#compute a matrix where each row are the
#terms to sum for each element of t
k <- K(outer(t, sample, FUN="-") / h)
#sum for each value of t
s <- rowSums(k)
#scale and return
return (s / h / n)
}
}
my_epanechnikov_kernel_density <- kernel_density(x, K=K)
#x_axis <- seq(5, 30, 0.01)
r_density <- density(x, kernel="gaussian", bw=2)
plot <- ggplot(data=data.frame(x=r_density$x, y=r_density$y, y_1=my_epanechnikov_kernel_density(r_density$x))) +
geom_line(aes(x=x, y=y, color="gaussian")) +
geom_line(aes(x=x, y=y_1, color="Sams epanechnikov"), linetype="dashed")
plot

#plot(density(x, kernel="gaussian", bw=2), main="Gaussian and epanechnikov kernels")
#points(x=x_axis, y=my_epanechnikov_kernel_density(x_axis), pch = 20, cex = 0.1)
#p
- How does the choice of kernel (Gaussian vs. Epanechnikov) affect the
density estimate? For both kernel estimators applied to this dataset,
what happens when we select \(h=1.5\)
versus \(h=2.5\)?
x <- c(12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4)
plot <- ggplot(data=data.frame(x=x), aes(x=x)) +
geom_density(bw=1.5, kernel="gaussian", aes(color="gausian h=1.5")) +
geom_density(bw=1.5, kernel="epanechnikov", aes(color="epanechnikov h=1.5")) +
geom_density(bw=2.5, kernel="gaussian", aes(color="gausian h=2.5")) +
geom_density(bw=2.5, kernel="epanechnikov", aes(color="epanechnikov h=2.5")) +
labs(title="Impact of Binwidth and Kernel on Density Approximation")
ggplotly(plot)
Here it seems that lower values of \(h\) correspond with bumpier approximations
while higher values of \(h\) correspond
with smoother approximations. The choice of kernel does not have as
large an impact as choice of \(h\), but
the gaussian kernel generally appears smoother than the epanechnikov
kernel for the same bandwidth.
LS0tCnRpdGxlOiAiQXNzaWdubWVudCAxOiBFc3RpbWF0aW5nIENERiBhbmQgUERGIgphdXRob3I6ICJTYW11ZWwgSm9obnNvbiIKaGVhZGVyLWluY2x1ZGVzOgogIC0gXHVzZXBhY2thZ2V7YW1zc3ltYn0KZGF0ZTogIiBEdWU6IDIvMy8yMDI2IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICB0b2NfZmxvYXQ6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiBubwogICAgdG9jX2NvbGxhcHNlZDogeWVzCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgc21vb3RoX3Njcm9sbDogeWVzCiAgICB0aGVtZTogbHVtZW4KICBwZGZfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIGZpZ193aWR0aDogMwogICAgZmlnX2hlaWdodDogMwogIHdvcmRfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAga2VlcF9tZDogeWVzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge2NzcywgZWNobyA9IEZBTFNFfQojVE9DOjpiZWZvcmUgewogIGNvbnRlbnQ6ICJUYWJsZSBvZiBDb250ZW50cyI7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgZm9udC1zaXplOiAxLjJlbTsKICBkaXNwbGF5OiBibG9jazsKICBjb2xvcjogbmF2eTsKICBtYXJnaW4tYm90dG9tOiAxMHB4Owp9CgoKZGl2I1RPQyBsaSB7ICAgICAvKiB0YWJsZSBvZiBjb250ZW50ICAqLwogICAgbGlzdC1zdHlsZTp1cHBlci1yb21hbjsKICAgIGJhY2tncm91bmQtaW1hZ2U6bm9uZTsKICAgIGJhY2tncm91bmQtcmVwZWF0Om5vbmU7CiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7Cn0KCmgxLnRpdGxlIHsgICAgLyogbGV2ZWwgMSBoZWFkZXIgb2YgdGl0bGUgICovCiAgZm9udC1zaXplOiAyMnB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7Cn0KCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovCiAgZm9udC1zaXplOiAxNXB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7CiAgY29sb3I6IG5hdnk7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CgpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOwogIGNvbG9yOiBEYXJrQmx1ZTsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KCmgxIHsgLyogSGVhZGVyIDEgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMjBweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGNlbnRlcjsKfQoKaDIgeyAvKiBIZWFkZXIgMiAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKaDMgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogICAgZm9udC1zaXplOiAxNnB4OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKaDQgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogICAgZm9udC1zaXplOiAxNHB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCi8qIEFkZCBkb3RzIGFmdGVyIG51bWJlcmVkIGhlYWRlcnMgKi8KLmhlYWRlci1zZWN0aW9uLW51bWJlcjo6YWZ0ZXIgewogIGNvbnRlbnQ6ICIuIjsKCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9CgouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQoKcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0KCn0KYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IAojIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dCBmaWxlcy4KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikKICAgbGlicmFyeShrbml0cikKfQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpCiAgIGxpYnJhcnkocGFuZGVyKQp9CmlmICghcmVxdWlyZSgiZ2dwbG90MiIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCiAgbGlicmFyeShnZ3Bsb3QyKQp9CmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQogIGxpYnJhcnkodGlkeXZlcnNlKQp9CgppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikKICBsaWJyYXJ5KHBsb3RseSkKfQojIyMjCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsICAgIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIAogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsICAgICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLgogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BCiAgICAgICAgICAgICAgICAgICAgICApICAKYGBgCiAKIFwKIAojIyAqKkFzc2lnbm1lbnQgT2JqZWN0aXZlcyoqIAoKKiBEZXZlbG9wIGEgY2xlYXIgdGVjaG5pY2FsIHVuZGVyc3RhbmRpbmcgb2Ygbm9ucGFyYW1ldHJpYyBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbiAoQ0RGKSBlc3RpbWF0aW9uIGFuZCB2YXJpb3VzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRvcnMuCgoqIFRyYW5zbGF0ZSBtYXRoZW1hdGljYWwgZm9ybXVsYXMgaW50byBSIGZ1bmN0aW9ucyBhbmQgYXBwbHkgdGhlbSB0byBzb2x2ZSByZWxhdGVkIHByb2JsZW1zLgoKKiBDcmVhdGUgZWZmZWN0aXZlIHZpc3VhbGl6YXRpb25zIHRvIGRlbW9uc3RyYXRlIHlvdXIgdW5kZXJzdGFuZGluZyBvZiBrZXkgY29uY2VwdHMgaW4gdGhlIGZvbGxvd2luZyBxdWVzdGlvbnMuCgoKClwKCiMjICoqUXVlc3Rpb24gMTogQ3VtdWxhdGl2ZSBEaXN0cmlidXRpb24gRnVuY3Rpb24gKENERikgRXN0aW1hdGlvbioqCgpUaGUgZm9sbG93aW5nIGZhaWx1cmUgdGltZXMgKGluIGhvdXJzKSB3ZXJlIG9ic2VydmVkIGZvciA4IGVsZWN0cm9uaWMgY29tcG9uZW50czoKCjxjZW50ZXI+IDIzLCA0NSwgNjcsIDg5LCAxMTIsIDE1NiwgMTg5LCAyNDUgIDwvY2VudGVyPgoKYSkgV3JpdGUgYW4gUiBmdW5jdGlvbiBpbXBsZW1lbnRpbmcgdGhlIEVDREYgJFxoYXR7Rn1fbih0KSQgYWNjb3JkaW5nIHRvIGl0cyBtYXRoZW1hdGljYWwgZGVmaW5pdGlvbi4gVmFsaWRhdGUgeW91ciBpbXBsZW1lbnRhdGlvbiB1c2luZyBSJ3MgZWNkZigpIGZ1bmN0aW9uIG9uIHRoZSBnaXZlbiBkYXRhLCB3aXRoIGNvbXBhcmlzb24gYmFzZWQgb24gdGhlaXIgc3RlcCBmdW5jdGlvbnMuCgpXZSBhcmUgZ2l2ZW4gdGhlIGZvbGxvd2luZyBkZWZpbml0aW9uIGZvciAkXGhhdHtGfV9uKHQpJAoKJCQgXGhhdHtGfV9uKHQpID0gXGZyYWN7MX17bn0gXHN1bV97aT0xfV57bn0gXG1hdGhiYntJfSBcbGVmdCh0X24gPCB0IFxyaWdodCkkJAp3aGVyZSwKCiQkIFxtYXRoYmJ7SX0odCkgPSAKICBcYmVnaW57Y2FzZXN9CiAgICAgIDAgJiAgdF9uID4gdFxcCiAgICAgIDEgJiB0X24gXGxlcSB0CiAgXGVuZHtjYXNlc30gCiQkCmBgYHtyfQpyYW5kb21fc2FtcGxlIDwtIGMoMjMsIDQ1LCA2NywgODksIDExMiwgMTU2LCAxODksIDI0NSkKCgpzYW1zX2VjZGYgPC0gZnVuY3Rpb24oeCkgewogIG4gPC0gbGVuZ3RoKHgpCiAgZnVuY3Rpb24gKHQpIHsKICAgIG1hdCA8LSBvdXRlcih4LCB0LCBGVU49Ijw9IikKICAgIHJldHVybiAoY29sU3VtcyhtYXQpIC8gbikKICB9Cn0KCiNzb21lIHRlc3RzIHRvIGNoZWNrIG15IGZ1bmN0aW9uIGJlaGF2ZXMgY29ycmVjdGx5CnJfbGFuZ19lY2RmIDwtIHN0YXRzOjplY2RmKHJhbmRvbV9zYW1wbGUpCnNhbV9lY2RmIDwtIHNhbXNfZWNkZihyYW5kb21fc2FtcGxlKQoKdGVzdF9wb2ludHMgPC0gc2VxKDIwLCAyNTAsIDEpCnJlc3VsdHMgPC0gcl9sYW5nX2VjZGYodGVzdF9wb2ludHMpID09IHNhbV9lY2RmKHRlc3RfcG9pbnRzKQpwcmludChwYXN0ZSgiZmFpbHM6ICIsIGxlbmd0aChyZXN1bHRzWyFyZXN1bHRzXSkpKQpwcmludChwYXN0ZSgicGFzc2VzOiAiLCBsZW5ndGgocmVzdWx0c1tyZXN1bHRzXSkpKQpgYGAKYGBge3J9CnhfcG9pbnRzIDwtIHNlcSgyMCwgMjUwLCAwLjUpCnBsb3QgPC0gZ2dwbG90KGRhdGE9ZGF0YS5mcmFtZSh4PXhfcG9pbnRzLCB5XzE9c2FtX2VjZGYoeF9wb2ludHMpLCB5XzI9cl9sYW5nX2VjZGYoeF9wb2ludHMpKSkgKwogICNnZW9tX3BvaW50KGFlcyh4PXgsIHk9eV8xLCBjb2xvcj0iU2FtJ3MgRUNERiIpKSArIAogICNnZW9tX3BvaW50KGFlcyh4PXgsIHk9eV8yLCBjb2xvcj0iUidzIEVDREYiKSkKICBnZW9tX2xpbmUoYWVzKHg9eCwgeT15XzEsIGNvbG9yPSJTYW0ncyBFQ0RGIikpICsgCiAgZ2VvbV9saW5lKGFlcyh4PXgsIHk9eV8yLCBjb2xvcj0iUidzIEVDREYiKSwgbGluZXR5cGU9ImRhc2hlZCIpICsKICBsYWJzKHg9ImZhaWx1cmUgdGltZXMiLCB5PSJjdW1sYXRpdmUgZGVuc2l0eSIpICsgCiAgbGFicyh0aXRsZT0iQ29tcGFyaXNvbiBvZiBFQ0RGIEltcGxpbWVudGF0aW9ucyIpCgpnZ3Bsb3RseShwbG90KQpgYGAKCgpiKSBBIGNvbGxlYWd1ZSBjbGFpbXMgdGhhdCB0aGUgcHJvYmFiaWxpdHkgb2YgZmFpbHVyZSBiZWZvcmUgMTAwIGhvdXJzIGlzIDAuNSBiYXNlZCBvbiB0aGVzZSBkYXRhLiBEbyB5b3UgYWdyZWU/IEV4cGxhaW4geW91ciByZWFzb25pbmcgdXNpbmcgdGhlIGVtcGlyaWNhbCBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbiAoRUNERikuCgpgYGB7cn0KcmFuZG9tX3NhbXBsZSA8LSBjKDIzLCA0NSwgNjcsIDg5LCAxMTIsIDE1NiwgMTg5LCAyNDUpCnJfZWNkZiA8LSBzdGF0czo6ZWNkZihyYW5kb21fc2FtcGxlKQpwcmludChyX2VjZGYoMTAwKSkKYGBgCkhlcmUgd2UgY29tcHV0ZSBFQ0RGKDEwMCBob3VycykuIFRoaXMgaXMgdGhlIHBvcnBvcnRpb24gb2YgZGF0YSB2YWx1ZXMgdGhhdCBhcmUgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIDEwMC4gVGhpcyB2YWx1ZSBhcHByb3hpbWF0ZXMgdGhlIHRydWUgQ0RGLCB0aGUgcHJvYmFiaWxpdHkgdGhhdCBhIGZhaWx1cmUgb2NjdXJzIGF0IG9yIGJlZm9yZSAxMDAgaG91cnMuIEZyb20gdGhlIGdpdmVuIGRhdGEgd2UgZ2V0IHRoYXQgRUNERigxMDApID0gMC41LCB3aGljaCBpcyB3aGF0IHRoZSBhIGNvbGxlYWd1ZSBjbGFpbXMgaXMgdGhlIHByb2JhYmlsaXR5IG9mIGZhaWx1cmUuIEkgYWdyZWUgdGhhdCBmcm9tIHRoZXNlIGRhdGEsIDAuNSBpcyBhIHJlYXNvbmFibGUgZXN0aW1hdGlvbiBmb3IgdGhlIHByb2JhYmlsaXR5IG9mIGZhaWx1cmUgYmVmb3JlIDEwMCBob3Vycy4KCgojIyAqKlF1ZXN0aW9uIDI6IERlbnNpdHkgRnVuY3Rpb24gRXN0aW1hdGlvbioqCgpDb25zaWRlciB0aGUgZm9sbG93aW5nIGZhaWx1cmUgdGltZXMgZnJvbSBhIG1lY2hhbmljYWwgc3lzdGVtOgoKPGNlbnRlcj4gMTIuMywgMTQuNywgMTUuMiwgMTYuOCwgMTguMSwgMTkuNCwgMjAuNiwgMjIuMywgMjMuOSwgMjUuNCA8L2NlbnRlcj4KCmEpIENyZWF0ZSBhIGhpc3RvZ3JhbSBvZiB0aGUgZGF0YSB1c2luZyAzIGVxdWFsbHkgc3BhY2VkIGJpbnMuIFdoYXQgaXMgdGhlIGVzdGltYXRlZCBkZW5zaXR5IGluIGVhY2ggYmluPyBEZXNjcmliZSB0aGUgc2hhcGUgb2YgdGhlIGhpc3RvZ3JhbSdzIGRpc3RyaWJ1dGlvbi4KCmBgYHtyfQpyYW5kb21fc2FtcGxlIDwtIGMoMTIuMywgMTQuNywgMTUuMiwgMTYuOCwgMTguMSwgMTkuNCwgMjAuNiwgMjIuMywgMjMuOSwgMjUuNCkKZF9mcmFtZSA8LSBkYXRhLmZyYW1lKHg9cmFuZG9tX3NhbXBsZSkKcGxvdCA8LSBnZ3Bsb3QoZGF0YT1kX2ZyYW1lLCBhZXMoeCA9IHgpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSBhZnRlcl9zdGF0KGRlbnNpdHkpKSxiaW5zPTMpCmdncGxvdGx5KHBsb3QpCmBgYApIZXJlLCB0aGUgaGlzdG9ncmFtIGhhcyBhIHJvdWdoIG1vdW5kIHNoYXBlIGNlbnRlcmVkIGF0IDE4Ljg1LgoKVGhlIGRlbnNpdHkgb2YgZWFjaCBiaW46CmJpbiAxLiAwLjA0NTgKYmluIDEuIDAuMDYxCmJpbiAzLiAwLjA0NTgKCgpiKSBXcml0ZSBhbiBSIGZ1bmN0aW9uIHRoYXQgY29tcHV0ZXMga2VybmVsIGRlbnNpdHkgZXN0aW1hdGVzIHVzaW5nIGEgR2F1c3NpYW4ga2VybmVsIHdpdGggJGg9MiQuIFZhbGlkYXRlIHlvdXIgaW1wbGVtZW50YXRpb24gYWdhaW5zdCBSJ3MgYnVpbHQtaW4gZGVuc2l0eSgpIGZ1bmN0aW9uLgoKJCQKXGhhdHtmfV9oKHQpID0gXGZyYWN7MX17bmh9XHN1bV97aT0xfV5uIEtcbGVmdCggXGZyYWN7dC10X2l9e2h9XHJpZ2h0KSwgXCBcIFx0ZXh0eyB3aGVyZSB9IFwgXCBLKHUpID0gXGZyYWN7MX17XHNxcnR7MlxwaX19IGVeey11XjIvMn0uCiQkCmBgYHtyfQoKCmtlcm5lbF9kZW5zaXR5IDwtIGZ1bmN0aW9uKHNhbXBsZSwgSz1kbm9ybSwgaD0yKSB7CiAgbiA8LSBsZW5ndGgoc2FtcGxlKQogIGZ1bmN0aW9uKHQsIGg9MikgewogICAgayA8LSBLKCBvdXRlcih0LCBzYW1wbGUsIEZVTj0iLSIpIC8gaCkKICAgIHMgPC0gcm93U3VtcyhrKQogICAgcmV0dXJuIChzIC8gbiAvIGgpCiAgfQp9Cgp4IDwtIGMoMTIuMywgMTQuNywgMTUuMiwgMTYuOCwgMTguMSwgMTkuNCwgMjAuNiwgMjIuMywgMjMuOSwgMjUuNCkKCm15X2dhdXNzaWFuX2tlcm5lbF9kZW5zaXR5IDwtIGtlcm5lbF9kZW5zaXR5KHgpCgpyX2RlbnNpdHkgPC0gZGVuc2l0eSh4LCBidz0yLCBrZXJuZWw9ImdhdXNzaWFuIikKCnBsb3QgPC0gZ2dwbG90KGRhdGE9ZGF0YS5mcmFtZSh4PXJfZGVuc2l0eSR4LCB5PXJfZGVuc2l0eSR5LCB5XzI9bXlfZ2F1c3NpYW5fa2VybmVsX2RlbnNpdHkocl9kZW5zaXR5JHgpKSkgKyAKICBnZW9tX2xpbmUoYWVzKHg9eCwgeT15XzIsIGNvbG9yPSJzYW0iKSkgKwogIGdlb21fbGluZShhZXMoeD14LCB5PXksIGNvbG9yPSJyIiksIGxpbmV0eXBlPSJkYXNoZWQiKQogIApnZ3Bsb3RseShwbG90KQoKYGBgCmMpIFdyaXRlIGEgY3VzdG9tIFIgZnVuY3Rpb24gdGhhdCBjb21wdXRlcyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZXMgdXNpbmcgdGhlIEVwYW5lY2huaWtvdiBrZXJuZWwgd2l0aCAkaD0yJC4gVmFsaWRhdGUgeW91ciBpbXBsZW1lbnRhdGlvbiBieSBjb21wYXJpbmcgcmVzdWx0cyB3aXRoIFIncyBidWlsdC1pbiBkZW5zaXR5KCkgZnVuY3Rpb24gZm9yIEdhdXNzaWFuIGtlcm5lbCBlc3RpbWF0aW9uLgoKJCQKXGhhdHtmfV9oKHQpID0gXGZyYWN7MX17bmh9XHN1bV97aT0xfV5uIEtcbGVmdCggXGZyYWN7dC10X2l9e2h9XHJpZ2h0KSwgXCBcIFx0ZXh0eyB3aGVyZSB9IFwgXCBLKHUpID0gXGZyYWN7M317NH0oMSAtIHVeMikgXCBcIFx0ZXh0eyBmb3IgfSBcIFwgfHV8IFxsZSAxLgokJAoKYGBge3J9Cgp4IDwtIGMoMTIuMywgMTQuNywgMTUuMiwgMTYuOCwgMTguMSwgMTkuNCwgMjAuNiwgMjIuMywgMjMuOSwgMjUuNCkKCksgPC0gZnVuY3Rpb24odSkgewogICAgbWFzayA8LSBhYnModSkgPD0gMQogICAgcmV0dXJuICgoKDMvNCkgKiAoMSAtIHVeMikpICogbWFzaykKfQoKa2VybmVsX2RlbnNpdHkgPC0gZnVuY3Rpb24oc2FtcGxlLCBLPWRub3JtKSB7CiAgZnVuY3Rpb24odCwgaD0yKSB7CiAgICBuIDwtIGxlbmd0aChzYW1wbGUpCiAgICAjY29tcHV0ZSBhIG1hdHJpeCB3aGVyZSBlYWNoIHJvdyBhcmUgdGhlCiAgICAjdGVybXMgdG8gc3VtIGZvciBlYWNoIGVsZW1lbnQgb2YgdAogICAgayA8LSBLKG91dGVyKHQsIHNhbXBsZSwgRlVOPSItIikgLyBoKQogICAgI3N1bSBmb3IgZWFjaCB2YWx1ZSBvZiB0CiAgICBzIDwtIHJvd1N1bXMoaykKICAgICNzY2FsZSBhbmQgcmV0dXJuCiAgICByZXR1cm4gKHMgLyBoIC8gbikKICB9Cn0KCm15X2VwYW5lY2huaWtvdl9rZXJuZWxfZGVuc2l0eSA8LSBrZXJuZWxfZGVuc2l0eSh4LCBLPUspCiN4X2F4aXMgPC0gc2VxKDUsIDMwLCAwLjAxKQoKcl9kZW5zaXR5IDwtIGRlbnNpdHkoeCwga2VybmVsPSJnYXVzc2lhbiIsIGJ3PTIpCnBsb3QgPC0gZ2dwbG90KGRhdGE9ZGF0YS5mcmFtZSh4PXJfZGVuc2l0eSR4LCB5PXJfZGVuc2l0eSR5LCB5XzE9bXlfZXBhbmVjaG5pa292X2tlcm5lbF9kZW5zaXR5KHJfZGVuc2l0eSR4KSkpICsKICBnZW9tX2xpbmUoYWVzKHg9eCwgeT15LCBjb2xvcj0iZ2F1c3NpYW4iKSkgKyAKICBnZW9tX2xpbmUoYWVzKHg9eCwgeT15XzEsIGNvbG9yPSJTYW1zIGVwYW5lY2huaWtvdiIpLCBsaW5ldHlwZT0iZGFzaGVkIikKICAKcGxvdAoKI3Bsb3QoZGVuc2l0eSh4LCBrZXJuZWw9ImdhdXNzaWFuIiwgYnc9MiksIG1haW49IkdhdXNzaWFuIGFuZCBlcGFuZWNobmlrb3Yga2VybmVscyIpCiNwb2ludHMoeD14X2F4aXMsIHk9bXlfZXBhbmVjaG5pa292X2tlcm5lbF9kZW5zaXR5KHhfYXhpcyksIHBjaCA9IDIwLCBjZXggPSAwLjEpCgojcApgYGAKCgoKZCkgSG93IGRvZXMgdGhlIGNob2ljZSBvZiBrZXJuZWwgKEdhdXNzaWFuIHZzLiBFcGFuZWNobmlrb3YpIGFmZmVjdCB0aGUgZGVuc2l0eSBlc3RpbWF0ZT8gRm9yIGJvdGgga2VybmVsIGVzdGltYXRvcnMgYXBwbGllZCB0byB0aGlzIGRhdGFzZXQsIHdoYXQgaGFwcGVucyB3aGVuIHdlIHNlbGVjdCAkaD0xLjUkIHZlcnN1cyAkaD0yLjUkPwoKYGBge3J9CnggPC0gYygxMi4zLCAxNC43LCAxNS4yLCAxNi44LCAxOC4xLCAxOS40LCAyMC42LCAyMi4zLCAyMy45LCAyNS40KQpwbG90IDwtIGdncGxvdChkYXRhPWRhdGEuZnJhbWUoeD14KSwgYWVzKHg9eCkpICsKICBnZW9tX2RlbnNpdHkoYnc9MS41LCBrZXJuZWw9ImdhdXNzaWFuIiwgYWVzKGNvbG9yPSJnYXVzaWFuIGg9MS41IikpICsKICBnZW9tX2RlbnNpdHkoYnc9MS41LCBrZXJuZWw9ImVwYW5lY2huaWtvdiIsIGFlcyhjb2xvcj0iZXBhbmVjaG5pa292IGg9MS41IikpICsKICBnZW9tX2RlbnNpdHkoYnc9Mi41LCBrZXJuZWw9ImdhdXNzaWFuIiwgYWVzKGNvbG9yPSJnYXVzaWFuIGg9Mi41IikpICsKICBnZW9tX2RlbnNpdHkoYnc9Mi41LCBrZXJuZWw9ImVwYW5lY2huaWtvdiIsIGFlcyhjb2xvcj0iZXBhbmVjaG5pa292IGg9Mi41IikpICsKICBsYWJzKHRpdGxlPSJJbXBhY3Qgb2YgQmlud2lkdGggYW5kIEtlcm5lbCBvbiBEZW5zaXR5IEFwcHJveGltYXRpb24iKQoKZ2dwbG90bHkocGxvdCkKCmBgYApIZXJlIGl0IHNlZW1zIHRoYXQgbG93ZXIgdmFsdWVzIG9mICRoJCBjb3JyZXNwb25kIHdpdGggYnVtcGllciBhcHByb3hpbWF0aW9ucyB3aGlsZSBoaWdoZXIgdmFsdWVzIG9mICRoJCBjb3JyZXNwb25kIHdpdGggc21vb3RoZXIgYXBwcm94aW1hdGlvbnMuIFRoZSBjaG9pY2Ugb2Yga2VybmVsIGRvZXMgbm90IGhhdmUgYXMgbGFyZ2UgYW4gaW1wYWN0IGFzIGNob2ljZSBvZiAkaCQsIGJ1dCB0aGUgZ2F1c3NpYW4ga2VybmVsIGdlbmVyYWxseSBhcHBlYXJzIHNtb290aGVyIHRoYW4gdGhlIGVwYW5lY2huaWtvdiBrZXJuZWwgZm9yIHRoZSBzYW1lIGJhbmR3aWR0aC4KCgoK